﻿// =====================================================================
//  File:		DynamicEntityPartialType.cs
//  Summary:	Defines concrete properties of DynamicEntity.
// =====================================================================
//
//  This file is part of the Microsoft CRM V4 SDK Code Samples.
//
//  Copyright (C) 2006 Microsoft Corporation.  All rights reserved.
//
//  This source code is intended only as a supplement to Microsoft
//  Development Tools and/or on-line documentation.  See these other
//  materials for detailed information regarding Microsoft code samples.
//
//  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//
// =====================================================================

namespace Acando.Dynamics.CRM.Silverlight.Library.CrmSdk
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Globalization;
    using System.Reflection;
    using System.Xml.Serialization;

    public class DynamicEntityExtended : DynamicEntity
    { 
        public DynamicEntityExtended() : base()
        {}
    }
   
    public partial class DynamicEntity
    {
        [XmlIgnore]
        public String Container { get; set; }

        
        public ObservableCollection<Property> PropertiesAsObservableCollection()
        { 
            ObservableCollection<Property> collection = new ObservableCollection<Property>();
            
            foreach (Property property in this.Properties)
                collection.Add(property);

            return collection;
        }

        public void Remove(string propertyName)
        {
            this.Indexer.RemoveProperty(propertyName);
        }

        public bool Contains(string propertyName)
        {
            Property property;
            return this.Indexer.TryGetProperty(propertyName, out property);
        }

        public object this[string propertyName]
        {
            get
            {
                Property property;
                if (Indexer.TryGetProperty(propertyName, out property))
                {
                    return PropertyFactory.GetPropertyValue(property);
                }

                throw new ArgumentException(string.Format("Property '{0}' is not found.", propertyName), "propertyName");
            }

            set
            {
                Property property;
                if (Indexer.TryGetProperty(propertyName, out property))
                {
                    PropertyFactory.SetPropertyValue(property, value);
                }
                else
                {
                    Indexer.AddNewProperty(propertyName, value);
                }
            }
        }

        private PropertyIndexer Indexer
        {
            get
            {
                if (_propertyIndexer == null)
                {
                    _propertyIndexer = new PropertyIndexer(this);
                }

                return _propertyIndexer;
            }
        }

        //[NonSerialized]
        private PropertyIndexer _propertyIndexer;

        private static class PropertyFactory
        {
            public static Property CreateInstance(string propertyName, object value)
            {
                Property p = null;
                if (value is CrmBoolean)
                {
                    p = new CrmBooleanProperty();
                }
                else if (value is CrmDateTime)
                {
                    p = new CrmDateTimeProperty();
                }
                else if (value is CrmDecimal)
                {
                    p = new CrmDecimalProperty();
                }
                else if (value is CrmFloat)
                {
                    p = new CrmFloatProperty();
                }
                else if (value is CrmMoney)
                {
                    p = new CrmMoneyProperty();
                }
                else if (value is CrmNumber)
                {
                    p = new CrmNumberProperty();
                }
                else if (value is Customer)
                {
                    p = new CustomerProperty();
                }
                else if (value is DynamicEntity[])
                {
                    p = new DynamicEntityArrayProperty();
                }
                else if (value is EntityNameReference)
                {
                    p = new EntityNameReferenceProperty();
                }
                else if (value is Key)
                {
                    p = new KeyProperty();
                }
                else if (value is Lookup)
                {
                    p = new LookupProperty();
                }
                else if (value is Owner)
                {
                    p = new OwnerProperty();
                }
                else if (value is Picklist)
                {
                    p = new PicklistProperty();
                }
                else if (value is Status)
                {
                    p = new StatusProperty();
                }
                else if (value is string)
                {
                    if (propertyName == "statecode")
                    {
                        p = new StateProperty();
                    }
                    else
                    {
                        p = new StringProperty();
                    }
                }
                else if (value is UniqueIdentifier)
                {
                    p = new UniqueIdentifierProperty();
                }
                else
                {
                    throw new ArgumentException("Property type is not supported.", "value");
                }

                p.Name = propertyName;
                SetPropertyValue(p, value);

                return p;
            }

            public static object GetPropertyValue(Property property)
            {
                PropertyInfo p = property.GetType().GetProperty("Value");

                return p.GetValue(property, null);
            }

            public static void SetPropertyValue(Property property, object value)
            {
                PropertyInfo p = property.GetType().GetProperty("Value");

                p.SetValue(property, value, null);
            }
        }

        private sealed class PropertyIndexer
        {
            public PropertyIndexer(DynamicEntity entity)
            {
                _entity = entity;
                _nameToIndex = new Dictionary<string, int>();
            }

            public bool TryGetProperty(string propertyName, out Property property)
            {
                int index;
                if (_nameToIndex.TryGetValue(propertyName, out index) && index < _entity.Properties.Length)
                {
                    property = _entity.Properties[index];

                    if (property.Name == propertyName)
                    {
                        return true;
                    }
                }

                ReIndex();
                if (_nameToIndex.TryGetValue(propertyName, out index))
                {
                    property = _entity.Properties[index];

                    return true;
                }

                property = null;
                return false;
            }

            public Property AddNewProperty(string propertyName, object value)
            {
                Property property = PropertyFactory.CreateInstance(propertyName, value);

                if (_entity.Properties == null)
                {
                    _entity.Properties = new Property[1];
                }
                else
                {
                    Array.Resize<Property>(ref _entity.propertiesField, _entity.Properties.Length + 1);
                }

                _entity.propertiesField[_entity.propertiesField.Length - 1] = property;

                _nameToIndex.Add(propertyName, _entity.propertiesField.Length - 1);

                return property;
            }

            public void RemoveProperty(string propertyName)
            {
                Property property;
                int index;
                if (_nameToIndex.TryGetValue(propertyName, out index))
                {
                    if (index < _entity.Properties.Length)
                    {
                        property = _entity.Properties[index];

                        if (property.Name == propertyName)
                        {
                            this.RemovePropertyAt(index);
                            return;
                        }
                    }
                }

                ReIndex();
                if (_nameToIndex.TryGetValue(propertyName, out index))
                {
                    this.RemovePropertyAt(index);
                }
            }

            private void RemovePropertyAt(int index)
            {
                // Move the last element into the index of the item removed.
                if (index != _entity.Properties.Length - 1)
                {
                    _entity.Properties[index] = _entity.Properties[_entity.Properties.Length - 1];
                }

                // Resize the array one smaller.
                Array.Resize<Property>(ref _entity.propertiesField, _entity.Properties.Length - 1);
            }

            private void ReIndex()
            {
                if (_entity.propertiesField != null)
                {
                    _nameToIndex = new Dictionary<string, int>(_entity.propertiesField.Length);

                    for (int i = 0; i < _entity.propertiesField.Length; i++)
                    {
                        Property property = _entity.propertiesField[i];

                        _nameToIndex.Add(property.Name, i);
                    }
                }
                else
                {
                    _nameToIndex = new Dictionary<string, int>();
                }
            }

            private DynamicEntity _entity;
            private Dictionary<string, int> _nameToIndex;
        }
    }
}